﻿//---------------------------------------------------------------------
// Copyright (c) 2007 Digerati Technologies, LLC.  All Rights Reserved.
//
// Release under the Microsoft Public License (Ms-PL).
//---------------------------------------------------------------------

using System;
using System.Diagnostics;
using System.IO;

namespace DigeratiTech.NDiagnostics
{
    /// <summary>
    /// This <see cref="DiagnosticTask"/> verifies that the specified file exists.
    /// </summary>
    public class FileTask : DiagnosticTask
    {
        #region Properties...

        /// <summary>
        /// Gets or sets the path to be validated.
        /// </summary>
        /// <value>The path to be validated.</value>
        /// <remarks>The specified path must exist for this task to succeed.</remarks>
        public string Path { get; set; }

        /// <summary>
        /// Gets or sets the version to be validated.
        /// </summary>
        /// <value>The version to be validated.</value>
        /// <remarks>A null version will cause the version to be ignored.</remarks>
        public string Version { get; set; }

        /// <summary>
        /// Gets or sets the minimum version to be validated.
        /// </summary>
        /// <value>The minimum version to be validated.</value>
        /// <remarks>A null version will cause the minimum version to be ignored.</remarks>
        public string MinVersion { get; set; }

        #endregion

        #region Constructors...

        /// <summary>
        /// Initializes a new instance of the <see cref="FileTask"/> class.
        /// </summary>
        /// <param name="group">The <see cref="TaskGroup"/> this task belongs to.</param>
        /// <param name="name">The task name.</param>
        /// <param name="path">The path to be validated.</param>
        /// <param name="version">[optional] The version number to verify.</param>
        /// <param name="minVersion">[optional]The minimum version to verify.</param>
        public FileTask(TaskGroup group, string name, string path, string version, string minVersion)
        {
            taskGroup = group;
            Name = name;
            Path = path;
            Version = version;
            MinVersion = minVersion;
            Status = TaskStatus.NotRun;
        }

        #endregion

        #region Methods...

        /// <summary>
        /// Executes the task.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if the task is successful; otherwise, <c>false</c>.
        /// </returns>
        public override bool Execute()
        {
            string fileVersion;

            Status = TaskStatus.Completed;
            Message = string.Empty;

            if (!File.Exists(Path))
            {
                Status = TaskStatus.Error;
                Message += ((Message.Length > 0) ? "\n" : "") + "The file '" + Path + "' could not be found.";
            }
            else if (Version != null)
            {
                fileVersion = FileVersionInfo.GetVersionInfo(Path).FileVersion;

                if (!IsVersionSame(Version, fileVersion))
                {
                    Status = TaskStatus.Error;
                    Message += ((Message.Length > 0) ? "\n" : "") + "The file version does not match. The actual version is '" + fileVersion + "' but should be '" + Version + "'.";
                }
            }
            else if (MinVersion != null)
            {
                fileVersion = FileVersionInfo.GetVersionInfo(Path).FileVersion;

                if (!IsVersionGreater(MinVersion, fileVersion))
                {
                    Status = TaskStatus.Error;
                    Message += ((Message.Length > 0) ? "\n" : "") + "The file version does not match. The actual version is '" + fileVersion + "' but should be at least '" + MinVersion + "'.";
                }
            }

            return (Message.Length == 0);
        }

        /// <summary>
        /// Determines whether the two version numbers passed in are the same.
        /// </summary>
        /// <param name="expectedVersion">The expected version number.</param>
        /// <param name="actualVersion">The actual version number.</param>
        /// <returns>
        /// 	<c>true</c> if the two version numbers match; otherwise, <c>false</c>.
        /// </returns>
        /// <remarks>The version numbers should be in the format of "M.m.R.B".</remarks>
        private bool IsVersionSame(string expectedVersion, string actualVersion)
        {
            bool isSame = true; // Assume they're the same until proven otherwise
            int tempExpectedValue;
            int tempActualValue;

            if (string.IsNullOrEmpty(expectedVersion) && !string.IsNullOrEmpty(actualVersion))
            {
                isSame = false;
            }
            else if (string.IsNullOrEmpty(actualVersion) && !string.IsNullOrEmpty(expectedVersion))
            {
                isSame = false;
            }
            else
            {
                // Create a set of arrays with minimum sizes
                string[] expectedArray = (expectedVersion + ".0.0.0.0").Split('.'); // e.g. 1.2.3.4.0.0.0.0
                string[] actualArray = (actualVersion + ".0.0.0.0").Split('.');     // e.g. 1.2.0.0.0.0.0.0

                // Compare the first four elements (all other elements are ignored)
                for (int index = 0; index < 4; index++)
                {
                    tempExpectedValue = 0;
                    tempActualValue = 0;

                    int.TryParse(expectedArray[index], out tempExpectedValue);
                    int.TryParse(actualArray[index], out tempActualValue);

                    if (tempActualValue != tempExpectedValue)
                    {
                        isSame = false;
                        break;
                    }
                }
            }

            return isSame;
        }

        /// <summary>
        /// Determines whether the <paramref name="actualVersion"/> is greater than or equal to the <paramref name="expectedVersion"/>.
        /// </summary>
        /// <param name="expectedVersion">The expected version number.</param>
        /// <param name="actualVersion">The actual version number.</param>
        /// <returns>
        /// 	<c>true</c> if the <paramref name="actualVersion"/> is greater than or equal to the <paramref name="expectedVersion"/>; otherwise, <c>false</c>.
        /// </returns>
        /// <remarks>The version numbers should be in the format of "M.m.R.B".</remarks>
        private bool IsVersionGreater(string expectedVersion, string actualVersion)
        {
            bool isGreater = true; // Assume they're the same until proven otherwise
            int tempExpectedValue;
            int tempActualValue;

            if (string.IsNullOrEmpty(expectedVersion) && !string.IsNullOrEmpty(actualVersion))
            {
                isGreater = false;
            }
            else if (string.IsNullOrEmpty(actualVersion) && !string.IsNullOrEmpty(expectedVersion))
            {
                isGreater = false;
            }
            else
            {
                // Create a set of arrays with minimum sizes
                string[] expectedArray = (expectedVersion + ".0.0.0.0").Split('.'); // e.g. 1.2.3.4.0.0.0.0
                string[] actualArray = (actualVersion + ".0.0.0.0").Split('.');     // e.g. 1.2.0.0.0.0.0.0

                // Convert the first four elements (all other elements are ignored) to integers
                for (int index = 0; index < 4; index++)
                {
                    tempExpectedValue = 0;
                    tempActualValue = 0;

                    int.TryParse(expectedArray[index], out tempExpectedValue);
                    int.TryParse(actualArray[index], out tempActualValue);

                    expectedArray[index] = tempExpectedValue.ToString();
                    actualArray[index] = tempActualValue.ToString();
                }

                // Compare the versions
                if (int.Parse(actualArray[0]) < int.Parse(expectedArray[0]))
                {
                    isGreater = false;
                }
                else if ((int.Parse(actualArray[1]) < int.Parse(expectedArray[1])) &&
                         (int.Parse(actualArray[0]) <= int.Parse(expectedArray[0])))
                {
                    isGreater = false;
                }
                else if ((int.Parse(actualArray[2]) < int.Parse(expectedArray[2])) &&
                         (int.Parse(actualArray[1]) <= int.Parse(expectedArray[1])) &&
                         (int.Parse(actualArray[0]) <= int.Parse(expectedArray[0])))
                {
                    isGreater = false;
                }
                else if ((int.Parse(actualArray[3]) < int.Parse(expectedArray[3])) &&
                         (int.Parse(actualArray[2]) <= int.Parse(expectedArray[2])) &&
                         (int.Parse(actualArray[1]) <= int.Parse(expectedArray[1])) &&
                         (int.Parse(actualArray[0]) <= int.Parse(expectedArray[0])))
                {
                    isGreater = false;
                }
            }

            return isGreater;
        }

        #endregion
    }
}
